/******************************************************************************
 * This program is a 100% Java Email Server.
 ******************************************************************************
 * Copyright (c) 2001-2011, Eric Daugherty (http://www.ericdaugherty.com)
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *   * Redistributions of source code must retain the above copyright
 *     notice, this list of conditions and the following disclaimer.
 *   * Redistributions in binary form must reproduce the above copyright
 *     notice, this list of conditions and the following disclaimer in the
 *     documentation and/or other materials provided with the distribution.
 *   * Neither the name of the copyright holder nor the
 *     names of its contributors may be used to endorse or promote products
 *     derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER ''AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************
 * For current versions and more information, please visit:
 * http://javaemailserver.sf.net/
 *
 * or contact the authors at:
 * java@ericdaugherty.com
 * andreaskyrmegalos@hotmail.com
 *
 ******************************************************************************
 * This program is based on the CSRMail project written by Calvin Smith.
 * http://crsemail.sourceforge.net/
 ******************************************************************************
 *
 * $Rev$
 * $Date$
 *
 ******************************************************************************/

package com.ericdaugherty.mail.server.utils;

//Java Imports
import java.io.IOException;
import java.io.InputStream;
import java.io.PushbackInputStream;

/**
 * This class adds to another InputStream the ability to read a line of bytes
 * off the underlying stream source. The assumption is that the source bytes
 * correspond to a sequence of characters. It is desirable in certain situations
 * to be able to read a delimited series of bytes rather than have them converted
 * to a stream of characters first, since it might be possible that the character
 * set is unknown at a stage of the process or that the overhead needed to use
 * the proper one is unacceptable or that it simply is not necessary to have the
 * bytes converted to characters. One such case is when JES reads a message from
 * an I/O source where the EOL is platform-dependent.
 *
 * @author Andreas Kyrmegalos
 */
public class DelimitedInputStream extends PushbackInputStream{

   /** A byte array corresponding to an EOL */
   protected final byte[] delimiter;

   /** A flag to indicate the size of the EOL */
   protected final boolean twoByteDelimiter;

   /** The size of the pushback buffer */
   protected final int maxBufferSize;

   /** A constructor using a fixed buffer size */
   public DelimitedInputStream(InputStream in) throws IOException {
      this(in,16);
   }

   /** A constructor using an application defined delimiter and a fixed buffer size */
   public  DelimitedInputStream(InputStream in, byte[] delimiter) throws IOException {
      this(in,16,delimiter);
   }

   /** A constructor using an application defined buffer size */
   public  DelimitedInputStream(InputStream in, int maxBufferSize) throws IOException {
      this(in,maxBufferSize,System.getProperty("line.separator").getBytes("US-ASCII"));
   }

   /** A constructor using an application defined buffer size and delimiter */
   public  DelimitedInputStream(InputStream in, int maxBufferSize, byte[] delimiter) throws IOException {
      super(in,maxBufferSize);
      this.maxBufferSize = maxBufferSize;
      this.delimiter = delimiter;
      this.twoByteDelimiter = delimiter.length>1;
   }

   /**
    * The method to extract a single line of bytes
    *
    * @return byte[] a line of bytes without a trailing EOL
    *
    */
   public byte[] readLine() throws IOException{

      byte[] buffer = new byte[maxBufferSize];
      int currentRead = read(buffer,0,maxBufferSize);
      if (currentRead==-1) return null;
      for (int i=0;i<currentRead;i++) {
         if (buffer[i]==delimiter[0]) {
            if (twoByteDelimiter) {
               if (i+1==currentRead) {
                  int nextByte = read();
                  if ((byte)nextByte==delimiter[1]) {
                     byte[] returnBuffer = new byte[i];
                     System.arraycopy(buffer, 0, returnBuffer, 0, i);
                     i++;
                     return returnBuffer;
                  }
                  else if (nextByte!=-1){
                     unread((byte)nextByte);
                  }
               }
               else if (buffer[i+1]==delimiter[1]) {
                  byte[] returnBuffer = new byte[i];
                  System.arraycopy(buffer, 0, returnBuffer, 0, i);
                  i++;
                  unread(buffer,i+1,currentRead-i-1);
                  return returnBuffer;
               }
            }
            else {
               byte[] returnBuffer = new byte[i];
               System.arraycopy(buffer, 0, returnBuffer, 0, i);
               unread(buffer,i+1,currentRead-i-1);
               return returnBuffer;
            }

         }
         if (i==currentRead-1) {
            if (currentRead==buffer.length) {
               byte[] moreBuffer = readLine();
               if (moreBuffer==null) {
                  return buffer;
               }
               else {
                  byte[] returnBuffer = new byte[moreBuffer.length+currentRead];
                  System.arraycopy(buffer, 0, returnBuffer, 0, currentRead);
                  System.arraycopy(moreBuffer,0,returnBuffer,currentRead,moreBuffer.length);
                  return returnBuffer;
               }
            }
            else {
               byte[] returnBuffer = new byte[currentRead];
                  System.arraycopy(buffer, 0, returnBuffer, 0, currentRead);
                  return returnBuffer;
            }
         }
      }

      //Should never get here, but the compiler complains otherwise
      return null;
   }

}
